package com.spz.demo.singleboot.shiro.filter;


import com.alibaba.fastjson.JSON;
import com.spz.demo.singleboot.common.WebConst;
import com.spz.demo.singleboot.util.BusinessUtil;
import com.spz.demo.singleboot.util.StringUtil;
import com.spz.demo.singleboot.vo.Code;
import com.spz.demo.singleboot.vo.Response;
import com.spz.demo.singleboot.exception.custom.RoleException;
import com.spz.demo.singleboot.util.JwtUtil;
import com.spz.demo.singleboot.util.ControllerUtil;
import com.spz.demo.singleboot.vo.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 重写shiro拦截器
 * 所有请求由此拦截器拦截
 */
@Slf4j//(topic = "USER_LOG")
@Component
public class ShiroLoginFilter extends AccessControlFilter {

    //由于项目启动时，Shiro加载比其他bean快，所以这里需要加入Lazy注解，在使用时再加载。否则会出现jwtUtil为null的情况
    @Autowired
    @Lazy
    private JwtUtil jwtUtil;

    @Override
    protected boolean isAccessAllowed(ServletRequest request,ServletResponse response, Object mappedValue) {
        // 判断请求是否是公共请求，通过请求的url判断
        if(BusinessUtil.isPublicRequest((HttpServletRequest) request)){
            return true;
        }
        return false;//  拒绝，统一交给 onAccessDenied 处理
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        // ========== 验证是否登录（检查json token） ==========
        if(StringUtil.isBlank(httpServletRequest.getHeader(WebConst.TOKEN))){
            // 返回JSON给请求方
            ControllerUtil.writeJSONToResponse(httpServletResponse,JSON.toJSONString(
                    Response.error(Code.TOKEN_ERROR,"[" + WebConst.TOKEN +  "] 不能为空，请将token存入header")
            ));
            return false;
        }
        String token = httpServletRequest.getHeader(WebConst.TOKEN);
        JwtToken jwtToken;
        try {
            jwtToken = jwtUtil.parseJwt(token);
        }catch (RoleException re){//出现异常，说明验证失败
            if(re.getMessage().equals(RoleException.MSG_TOKEN_ERROR)){//token错误异常
                ControllerUtil.writeJSONToResponse((HttpServletResponse) response,JSON.toJSONString(
                        Response.error(Code.TOKEN_ERROR))
                );//返回json
            }else{//token过期异常
                ControllerUtil.writeJSONToResponse((HttpServletResponse) response,JSON.toJSONString(
                        Response.error(Code.EXPIRE_TOKEN))
                );
            }
            return false;
        }

        // ========== token自动刷新 ==========
        // 如果不使用自动刷新机制，则需要前端定时发送刷新token请求
        if(jwtToken.getIsFlushed()){//需要刷新token
            httpServletResponse.setHeader(WebConst.TOKEN,jwtToken.getToken());// 更新到response
            log.debug("刷新了token");
        }


        // ========== 检查角色&权限 ==========
        if(!jwtToken.hasUrl(((HttpServletRequest) request).getRequestURI())){
            ControllerUtil.writeJSONToResponse((HttpServletResponse) response,JSON.toJSONString(
                    Response.error(Code.ROLE_ERROR)
            ));
            log.debug("权限验证未通过");
            return false;
        }else{//登录验证通过
            log.debug("权限验证通过");
            return true;
        }
    }
}
